/*
 *
 *
 */
#include <string.h>
#include "gkt_debug.h"
#include "gkt_gpio.h"
#include "gkt_led.h"
#include "gkt_os.h"

#ifdef GKT_CONFIG_LED_DESC_TABLE

typedef struct {
	uint32_t	pin;
	uint32_t	on_level;
	uint32_t	current_ds;
} led_desc_s;
#undef GKT_LED_DESC
#define GKT_LED_DESC(_name, _pin, _on_level, _current_ds)	\
	{	\
		.pin = _pin,	\
		.on_level	= _on_level,	\
		.current_ds	= _current_ds,	\
	}
static const led_desc_s sc_led_descs[] = {
	GKT_CONFIG_LED_DESC_TABLE
};

static const uint32_t sc_led_gpio_flags[] = {
	GKT_GPIO_F_OUTPUT | GKT_GPIO_F_OUTPUT_HIGH,//| GKT_GPIO_F_PULL_UP
	GKT_GPIO_F_OUTPUT,// | GKT_GPIO_F_PULL_DOWN
};

typedef struct {
	gkt_soft_timer_t	timer;
	int	timer_start;
	int	forward;
	uint32_t	frame_index;
	uint32_t	cycle_index;
} led_flicker_state_s;

typedef struct {
#if GKT_LED_FLICKER_ENABLE
	gkt_mutex_t	mutex;
	gkt_led_flicker_params_s	flicker_params;
	led_flicker_state_s	flicker_state;
#endif
	uint32_t	on_led_bits;
} led_instance_s;
static led_instance_s s_led_instance;

#if GKT_LED_FLICKER_ENABLE
#define LED_MUTEX_LOCK()	\
	gkt_mutex_lock(s_led_instance.mutex, GKT_INFINITE);
#define LED_MUTEX_UNLOCK()	\
	gkt_mutex_unlock(s_led_instance.mutex);
#else
#define LED_MUTEX_LOCK()	do {} while (0)
#define LED_MUTEX_UNLOCK()	do {} while (0)
#endif

static void led_lighten(uint32_t off_led_bits, uint32_t on_led_bits)
{
	uint32_t mask, id;

	for (id = 0; id < GKT_LED_ID_MAX; id++) {
		mask = 1U << id;
		if (off_led_bits & mask) {
			gkt_gpio_set_pin_level(sc_led_descs[id].pin, 
				!sc_led_descs[id].on_level);
		}
		else if (on_led_bits & mask) {
			gkt_gpio_set_pin_level(sc_led_descs[id].pin, 
				sc_led_descs[id].on_level);
		}
	}
}

void gkt_led_lighten(uint32_t off_led_bits, uint32_t on_led_bits)
{
	uint32_t mask;
	
	if (off_led_bits || on_led_bits) {
		mask = (1U << GKT_LED_ID_MAX) - 1;

		LED_MUTEX_LOCK();
		s_led_instance.on_led_bits |= on_led_bits & mask;
		s_led_instance.on_led_bits &= ~(off_led_bits & mask);
		led_lighten(~s_led_instance.on_led_bits, s_led_instance.on_led_bits);
		LED_MUTEX_UNLOCK();
	}
}

#if GKT_LED_FLICKER_ENABLE
static void led_flick_show_frame(uint32_t frame_index)
{
	uint32_t on_led_bits, off_led_bits;

	on_led_bits = s_led_instance.flicker_params.frame_led_bits[frame_index];
	on_led_bits &= s_led_instance.flicker_params.all_led_bits;
	off_led_bits = ~on_led_bits & s_led_instance.flicker_params.all_led_bits;
	led_lighten(off_led_bits, on_led_bits);
}
static void led_flick_stop(uint32_t end_action)
{
	if (s_led_instance.flicker_state.timer_start) {
		s_led_instance.flicker_state.timer_start = 0;
		gkt_soft_timer_stop(s_led_instance.flicker_state.timer);

		if (end_action & GKT_LED_FLICKER_F_END_RESUME_ORIGIN)
			led_lighten(~s_led_instance.on_led_bits, s_led_instance.on_led_bits);
		else if (end_action & GKT_LED_FLICKER_F_END_LAST_FRAME)
			led_flick_show_frame(s_led_instance.flicker_params.frame_nums - 1);
		else if (end_action & GKT_LED_FLICKER_F_END_LIGHTEN_ALL)
			led_lighten(0, s_led_instance.flicker_params.all_led_bits);
		else
			led_lighten(s_led_instance.flicker_params.all_led_bits, 0);
	}
}
static void led_flick_timer_callback(void *params)
{
	gkt_unused(params);

	LED_MUTEX_LOCK();

	if (s_led_instance.flicker_state.timer_start) {
		if (s_led_instance.flicker_state.forward) {	/* forward direction */
			s_led_instance.flicker_state.frame_index++;
			if (s_led_instance.flicker_state.frame_index == s_led_instance.flicker_params.frame_nums) {
				/* reach the max frame nums of current cycle */
				if (s_led_instance.flicker_params.flags & GKT_LED_FLICKER_F_REVERSE_BACK) {
					/* support reverse back from the last frame */
					s_led_instance.flicker_state.forward = 0;	/* clear forward direction state */
					s_led_instance.flicker_state.frame_index -= 2;
				}
				else {
					/* does not support reverse back from the last frame */
					s_led_instance.flicker_state.cycle_index++;	/* cycles increase */
					if (s_led_instance.flicker_state.cycle_index == s_led_instance.flicker_params.cycles) {
						/* finish */
						led_flick_stop(s_led_instance.flicker_params.flags);
						if (s_led_instance.flicker_params.notify_fn) {
							(*s_led_instance.flicker_params.notify_fn)(GKT_LED_FLICKER_ST_DONE,
										s_led_instance.flicker_params.private_data);
						}
						LED_MUTEX_UNLOCK();

						return;
					}

					/* not finish, start a new cycle from the frame-0 */
					s_led_instance.flicker_state.frame_index = 0;
				}
			}
		}
		else {
			if (s_led_instance.flicker_state.frame_index > 0)
				s_led_instance.flicker_state.frame_index--;
			else {
				s_led_instance.flicker_state.cycle_index++;
				if (s_led_instance.flicker_state.cycle_index == s_led_instance.flicker_params.cycles) {
					led_flick_stop(s_led_instance.flicker_params.flags);
					if (s_led_instance.flicker_params.notify_fn) {
						(*s_led_instance.flicker_params.notify_fn)(GKT_LED_FLICKER_ST_DONE,
									s_led_instance.flicker_params.private_data);
					}
					LED_MUTEX_UNLOCK();

					return;
				}

				s_led_instance.flicker_state.forward = 1;
				s_led_instance.flicker_state.frame_index = 1;
			}
		}

		led_flick_show_frame(s_led_instance.flicker_state.frame_index);
	}

	LED_MUTEX_UNLOCK();
}

int gkt_led_flicker(const gkt_led_flicker_params_s *params)
{
	int retval;

	if (!params) {	/* stop current flicker */
		LED_MUTEX_LOCK();
		led_flick_stop(GKT_LED_FLICKER_F_END_RESUME_ORIGIN);
		LED_MUTEX_UNLOCK();

		retval = GKT_SUCCESS;
	}
	else if ((params->cycles > 0) && (params->frame_time > 0)
		&& (params->frame_nums >= 2) && params->frame_led_bits
		&& params->all_led_bits)
	{	/* start new flicker */
		if (!s_led_instance.flicker_state.timer) {
			s_led_instance.flicker_state.timer = gkt_soft_timer_create(
						GKT_SOFT_TIMER_PERIODIC,
						led_flick_timer_callback,
						NULL);
			if (!s_led_instance.flicker_state.timer) {
				gkt_error("led_flick: create soft timer failed!\n");
				return GKT_ECREATE;
			}
		}

		LED_MUTEX_LOCK();
		led_flick_stop(GKT_LED_FLICKER_F_END_LIGHTEN_NONE);

		memcpy(&s_led_instance.flicker_params, params, sizeof(gkt_led_flicker_params_s));
		s_led_instance.flicker_state.forward = 1;
		s_led_instance.flicker_state.frame_index = 0;
		s_led_instance.flicker_state.cycle_index = 0;
		led_flick_show_frame(s_led_instance.flicker_state.frame_index);
		LED_MUTEX_UNLOCK();

		retval = gkt_soft_timer_start(s_led_instance.flicker_state.timer,
						s_led_instance.flicker_params.frame_time);
		if (GKT_SUCCESS == retval)
			s_led_instance.flicker_state.timer_start = 1;
		else {
			gkt_error("led_flick: start soft tiemr failed %d\n", retval);
		}
	}
	else
		retval = GKT_EPARAM;

	return retval;
}
#endif

int gkt_led_init(void)
{
	uint32_t i, flags;
	int retval;

#if GKT_LED_FLICKER_ENABLE
	s_led_instance.flicker_state.timer_start = 0;
	s_led_instance.mutex = gkt_mutex_create(0);
	if (!s_led_instance.mutex) {
		gkt_error("led_init: create mutex failed!\n");
		return GKT_ECREATE;
	}
#endif

	s_led_instance.on_led_bits = 0;
	for (i = 0; i < GKT_LED_ID_MAX; i++) {
		flags = sc_led_gpio_flags[!!sc_led_descs[i].on_level];
		flags |= sc_led_descs[i].current_ds & GKT_GPIO_F_DS_MASK;
		retval = gkt_gpio_config(sc_led_descs[i].pin, flags, NULL);
		if (GKT_SUCCESS == retval) {
			gkt_gpio_set_pin_level(sc_led_descs[i].pin, 
				!sc_led_descs[i].on_level);
		}
		else {
			gkt_error("led_init: config led-%u gpio(0x%x) failed %d\n", 
						i, sc_led_descs[i].pin, retval);
		}
	}

	return GKT_SUCCESS;
}

void gkt_led_standby(void)
{
	uint32_t i;
	//int retval;

	gkt_soft_timer_stop(s_led_instance.flicker_state.timer);
	for (i = 0; i < GKT_LED_ID_MAX; i++) {
		//retval = gkt_gpio_config(sc_led_descs[i].pin, 
		//				GKT_GPIO_F_INPUT, NULL);
		//if (retval != GKT_SUCCESS) {
			//gkt_error("gkt_led_standby: config led-%u gpio(0x%x) failed %d\n", 
			//		i, sc_led_descs[i].pin, retval);
		//}
		gkt_gpio_set_pin_level(sc_led_descs[i].pin, !sc_led_descs[i].on_level);
		//gkt_printf("led[%d]=%d\n",i, !sc_led_descs[i].on_level);
	}
}

#endif

